package cn.edu.dgut.css.sai.excel.alibaba;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.view.AbstractView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.function.BiConsumer;

/**
 * 本Demo演示如何使用Spring MVC创建Excel视图。
 * 通过使用Spring MVC ContentNegotiation（内容协商），我们可以生成同一资源的多个视图。
 * Spring MVC 视图的功能就是构造HttpServletResponse，生成http请求的响应。
 * 本Demo使用阿里巴巴开源的EasyExcel创建Excel视图，可以根据url的后缀自动选择生成excel文档类型（.xls或.xlsx格式）。
 * 注意：生成大型excel文档时，使用流式xlsx视图是比较好选择。流视图使用较少的内存，可以提高大型excel文档的性能。
 * 本Demo定义了一个新的视图{@link EasyExcelView}封装所有功能。
 * *
 * 参考：
 * https://memorynotfound.com/spring-mvc-excel-view-example/
 * https://gitee.com/dgut-sai/spring-mvc-advanced-course
 * http://zetcode.com/springboot/servepdf/
 *
 * @author Sai 东莞理工学院 网络空间安全学院 lizhx@dgut.edu.cn
 * Created by Sai on 2020/4/11.
 */
@SuppressWarnings("unused")
public class EasyExcelView extends AbstractView {

    private String filename;
    private ExcelTypeEnum excelTypeEnum = ExcelTypeEnum.XLS;
    private BiConsumer<Map<String, Object>, ExcelWriterBuilder> consumer;

    public EasyExcelView() {
        setContentType("application/vnd.ms-excel");
    }

    public EasyExcelView(BiConsumer<Map<String, Object>, ExcelWriterBuilder> consumer) {
        this();
        this.consumer = consumer;
    }

    public static EasyExcelView buildView(BiConsumer<Map<String, Object>, ExcelWriterBuilder> consumer) {
        return new EasyExcelView(consumer);
    }

    public EasyExcelView fileName(String filename) {
        this.setFilename(filename);
        return this;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public EasyExcelView buildExcelDocument(BiConsumer<Map<String, Object>, ExcelWriterBuilder> consumer) {
        this.consumer = consumer;
        return this;
    }

    public void setExcelTypeEnum(ExcelTypeEnum excelTypeEnum) {
        this.excelTypeEnum = excelTypeEnum;
    }

    @Override
    protected boolean generatesDownloadContent() {
        return true;
    }

    @Override
    protected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 根据url的后缀判断生成excel文档的类型(xls 或 xlsx)
        if (request.getServletPath().endsWith(ExcelTypeEnum.XLSX.getValue())) setExcelTypeEnum(ExcelTypeEnum.XLSX);
        response.setContentType(getContentType());
        ExcelWriterBuilder excelWriterBuilder = createExcelWriterBuilder(response);
        doExcelWriter(model, excelWriterBuilder);
    }

    protected ExcelWriterBuilder createExcelWriterBuilder(HttpServletResponse response) throws IOException {
        // 改变文件名
        if (!StringUtils.isEmpty(filename))
            response.setHeader("Content-Disposition", "attachment; filename=\"".concat(filename).concat(excelTypeEnum.getValue()).concat("\""));

        return EasyExcel.write(response.getOutputStream())
                .excelType(excelTypeEnum)
                .autoCloseStream(true);
    }

    protected void doExcelWriter(Map<String, Object> model, ExcelWriterBuilder excelWriterBuilder) {
        this.consumer.accept(model, excelWriterBuilder);
    }

}
